#include "TimeSeries.h"
#include "LinkedSort.h"

#include <QtCore/QFile>
#include <QtCore/QTextStream>
#include <QtCore/QString>
#include <QtCore/QStringList>

#include <iostream>
#include <cmath>
#include <ctime>
#include <cstdlib>

#ifndef M_PI
    #define M_PI 3.14159265358979323846
#endif

using namespace spectClean;

TimeSeries::TimeSeries()
{
}

TimeSeries::~TimeSeries()
{
}

void TimeSeries::clear()
{
    _time.clear();
    _amp.clear();
}

void TimeSeries::resize(const unsigned n)
{
    _time.resize(n, 0.0);
    _amp.resize(n, 0.0);
}


int TimeSeries::load(const QString& filename)
{
    // Open the file for reading.
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return 0;

    // Construct a QTextStream object to read the file.
    QTextStream in(&file);
    while (!in.atEnd())
    {
        QStringList values = in.readLine().split(' ');
        _time.push_back(values[0].toDouble());
        _amp.push_back(values[1].toDouble());
    }

    // Close the file.
    file.close();

    // Return the number of time series values.
    return (int)_time.size();
}


void TimeSeries::save(const QString& filename) const
{
    if (QFile::exists(filename))
        QFile::remove(filename);

    QFile file(filename);
    if (!file.open(QIODevice::WriteOnly))
        return;

    QTextStream out(&file);
    out.setNumberFlags(QTextStream::ForceSign);
    out.setRealNumberNotation(QTextStream::FixedNotation);
    out.setRealNumberPrecision(8);
    out.setFieldWidth(4);
    for (unsigned i = 0; i < _time.size(); ++i)
    {
        out << i << " ";
        out << _time[i] << " ";
        out << _amp[i] << endl;
    }

    file.close();
}

void TimeSeries::shift_times(const double offset)
{
    for (unsigned i = 0; i < _time.size(); ++i)
        _time[i] -= offset;
}


void TimeSeries::generateEven(const unsigned num_times, const double start_time,
        const double end_time, const std::vector<SpectrumComponent>& components)
{
    const unsigned num_freqs    = components.size();
    const double period         = end_time - start_time;
    const double time_increment =  period / (num_times - 1);

    resize(num_times);

    for (unsigned j = 0; j < num_times; ++j)
    {
        _time[j] = j * time_increment;

        for (unsigned i = 0; i < num_freqs; ++i)
        {
            const double amp  = components[i].amplitude();
            const double freq = components[i].frequency();
            _amp[j] += amp * std::sin(2.0 * M_PI * freq * _time[j]);
        }
    }
}


void TimeSeries::generateRandom(const unsigned num_times, const double start_time,
        const double end_time, const std::vector<SpectrumComponent>& components)
{
    const unsigned num_freqs = components.size();
    const double period      = end_time - start_time;
    const int scaled_period  = static_cast<int>(1.0e6 * period);

    resize(num_times);

    std::srand(std::time(NULL));

    for (unsigned j = 0; j < num_times; ++j)
    {
        _time[j] = start_time + (std::rand() % scaled_period) / 1.0e6;

        for (unsigned i = 0; i < num_freqs; ++i)
        {
            const double amp  = components[i].amplitude();
            const double freq = components[i].frequency();
            _amp[j] += amp * std::sin(2.0 * M_PI * freq * _time[j]);
        }
    }

    LinkedSort::lSort(_time, _amp);
}



void TimeSeries::mean(double& time, double& amp) const
{
    const unsigned num_times = _time.size();
    const double * t = &_time[0];
    const double * a = &_amp[0];
    double sumTime = 0.0, sumAmp = 0.0;
    for (unsigned i = 0; i < num_times; ++i)
    {
        sumTime += t[i];
        sumAmp += a[i];
    }
    time = sumTime / (double)num_times;
    amp = sumAmp / (double)num_times;
}

